package com.dmai.capacity.platform.user.service.impl;

import java.util.*;
import java.util.concurrent.TimeUnit;

import javax.annotation.Resource;

import cn.hutool.core.util.RandomUtil;
import com.dmai.capacity.platform.common.constant.CodeEnum;
import com.dmai.capacity.platform.common.context.TenantContextHolder;
import com.dmai.capacity.platform.common.dto.PageResultData;
import com.dmai.capacity.platform.constants.ExceptionEnum;
import com.dmai.capacity.platform.constants.RedisKeyConstant;
import com.dmai.capacity.platform.log.trace.MDCTraceUtils;
import com.dmai.capacity.platform.user.mapper.SysUserDao;
import com.dmai.capacity.platform.user.mapper.SysUserRoleDao;
import com.dmai.capacity.platform.user.model.SysFunctionPermission;
import com.dmai.capacity.platform.user.model.SysUserRole;
import com.dmai.capacity.platform.user.pojo.request.PasswordEmailRequest;
import com.dmai.capacity.platform.user.pojo.request.PasswordResetRequest;
import com.dmai.capacity.platform.user.service.IEmailService;
import com.dmai.capacity.platform.user.service.ISysFunctionPermissionService;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.dmai.capacity.platform.common.constant.CommonConstant;
import com.dmai.capacity.platform.common.dto.PageResult;
import com.dmai.capacity.platform.common.exception.BusinessException;
import com.dmai.capacity.platform.common.model.LoginAppUser;
import com.dmai.capacity.platform.common.model.SysUser;
import com.dmai.capacity.platform.user.service.ISysUserService;
import lombok.extern.slf4j.Slf4j;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;


@Slf4j
@Service
public class SysUserServiceImpl implements ISysUserService {

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private SysUserDao userDao;

    @Resource
    private SysUserRoleDao userRoleDao;

    @Autowired
    private ISysFunctionPermissionService functionPermissionService;

    @Autowired
    private IEmailService emailService;

    @Autowired
    private TemplateEngine templateEngine;

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;


    @Override
    public LoginAppUser findByUsername(String username) {
        SysUser sysUser = this.selectByUsername(username);
        return getLoginAppUser(sysUser);
    }

    @Override
    public LoginAppUser findByOpenId(String username) {
        SysUser sysUser = this.selectByOpenId(username);
        return getLoginAppUser(sysUser);
    }

    @Override
    public LoginAppUser findByMobile(String phone) {
        SysUser sysUser = this.selectByMobile(phone);
        return getLoginAppUser(sysUser);
    }

    @Override
    public SysUser selectByUserid(Long userid) {
        SysUser user = userDao.findById(userid);
        return user;
    }

    /**
     * 根据用户名查询用户
     *
     * @param username
     * @return
     */
    @Override
    public SysUser selectByUsername(String username) {
        SysUser user = userDao.findByUsername(username);
        return user;
    }

    /**
     * 根据手机号查询用户
     *
     * @param mobile
     * @return
     */
    @Override
    public SysUser selectByMobile(String mobile) {
        Map<String, Object> param = new HashMap<>();
        param.put("mobile", mobile);
        List<SysUser> users = userDao.findList(param);
        return getUser(users);
    }

    /**
     * 根据openId查询用户
     *
     * @param openId
     * @return
     */
    @Override
    public SysUser selectByOpenId(String openId) {
        Map<String, Object> param = new HashMap<>();
        param.put("openId", openId);
        List<SysUser> users = userDao.findList(param);
        return getUser(users);
    }

    /**
     * 根据userId查询用户
     *
     * @param userId
     * @return
     */
    @Override
    public LoginAppUser findByUserId(Long userId) {
        SysUser sysUser = userDao.findById(userId);
        return getLoginAppUser(sysUser);
    }

    @Override
    public boolean sendPasswordResetEmail(PasswordEmailRequest item) {
        SysUser model = userDao.findByUsername(item.getUsername());
        if (model == null ) {
            throw new BusinessException(ExceptionEnum.ACCOUNT_NOT_EXIST.getCode(), ExceptionEnum.ACCOUNT_NOT_EXIST.getMsg());
        }
        if (!item.getEmail().equals(model.getEmail())||!item.getUsername().equals(model.getUsername())) {
            throw new BusinessException(ExceptionEnum.ACCOUNT_EMAIL_NOT_MATCH.getCode(), ExceptionEnum.ACCOUNT_EMAIL_NOT_MATCH.getMsg());
        }

        // 发送邮件
        sendEmail(model);

        return true;
    }

    @Override
    public boolean resetPassword(PasswordResetRequest item) {
        SysUser model = userDao.findByUsername(item.getUsername());
        if (model == null && !item.getUsername().equals(model.getUsername())) {
            throw new BusinessException(ExceptionEnum.ACCOUNT_NOT_EXIST.getCode(), ExceptionEnum.ACCOUNT_NOT_EXIST.getMsg());
        }
        if (!item.getEmail().equals(model.getEmail())) {
            throw new BusinessException(ExceptionEnum.ACCOUNT_EMAIL_NOT_MATCH.getCode(), ExceptionEnum.ACCOUNT_EMAIL_NOT_MATCH.getMsg());
        }
        String code = (String) redisTemplate.opsForValue().get(RedisKeyConstant.CACHE_FORGOT_PWD_VERIFY_CODE_PREFIX + item.getEmail());
        if (!item.getVerifyCode().equals(code)) {
            throw new BusinessException(ExceptionEnum.VERIFY_CODE_ERROR.getCode(), ExceptionEnum.VERIFY_CODE_ERROR.getMsg());
        }
        SysUser user = new SysUser();
        user.setId(model.getId());
        user.setPassword(passwordEncoder.encode(item.getNewPassword()));

        int r = userDao.updateByOps(user);
        if (r > 0) {
            // 删除缓存
            redisTemplate.delete(RedisKeyConstant.CACHE_FORGOT_PWD_VERIFY_CODE_PREFIX);

            return true;

        } else {
            return false;
        }
    }

    @Override
    public boolean save(SysUser user) {
        boolean flag = false;
        LoginAppUser orig = this.findByUsername(user.getUsername());
        if (orig != null) {
            throw new BusinessException(String.format("%s用户已存在", user.getUsername()));
        }
        if (StringUtils.isNotEmpty(user.getPassword()) && !user.getPassword().startsWith("{bcrypt}")) {
            user.setPassword(passwordEncoder.encode(user.getPassword()));
        } else if (StringUtils.isEmpty(user.getPassword())) {
            user.setPassword(passwordEncoder.encode(CommonConstant.DEF_USER_PASSWORD));
        }
        if (user.getId() == null) {
            user.setId(Long.valueOf(MDCTraceUtils.createTraceId()));
        }
        user.setCreateTime(new Date());
        if (user.getStatus() == null) {
            user.setStatus(1);
        }
        if (user.getEnabled() == null) {
            user.setEnabled(Boolean.TRUE);
        }
        user.setDeleted(Boolean.FALSE);

        userDao.save(user);

        if (StringUtils.isNotEmpty(user.getRoleIds())) {
            // 更新角色
            userRoleDao.deleteByUser(user.getId());
            if (!StringUtils.isEmpty(user.getRoleIds())) {
                SysUserRole item;
                for (String roleId : user.getRoleIds().split(",")) {
                    item = new SysUserRole();
                    item.setRoleId(Long.valueOf(roleId));
                    item.setUserId(user.getId());
                    userRoleDao.save(item);
                }
            }
        }

        return flag;
    }

    @Override
    public boolean update(SysUser user) {
        boolean flag = false;

        if (StringUtils.isNotEmpty(user.getPassword()) && !user.getPassword().startsWith("{bcrypt}")) {
            user.setPassword(passwordEncoder.encode(user.getPassword()));
        } else if (StringUtils.isEmpty(user.getPassword())) {
            user.setPassword(passwordEncoder.encode(CommonConstant.DEF_USER_PASSWORD));
        }
        if (user.getStatus() == null) {
            user.setStatus(1);
        }
        if (user.getEnabled() == null) {
            user.setEnabled(Boolean.TRUE);
        }
        user.setDeleted(Boolean.FALSE);

        userDao.updateByOps(user);

        if (StringUtils.isNotEmpty(user.getRoleIds())) {
            // 更新角色
            userRoleDao.deleteByUser(user.getId());
            if (!StringUtils.isEmpty(user.getRoleIds())) {
                SysUserRole item;
                for (String roleId : user.getRoleIds().split(",")) {
                    item = new SysUserRole();
                    item.setRoleId(Long.valueOf(roleId));
                    item.setUserId(user.getId());
                    userRoleDao.save(item);
                }
            }
        }

        return flag;
    }


    // 忘记密码发邮件
    private void sendEmail(SysUser target) {
        log.info("开始发送重置密码邮件：{}...", target.getEmail());
        Context context = new Context();

        // 生成验证码
        String code = RandomUtil.randomNumbers(6);
        redisTemplate.opsForValue().set(RedisKeyConstant.CACHE_FORGOT_PWD_VERIFY_CODE_PREFIX + target.getEmail(), code, 10, TimeUnit.MINUTES);

        context.setVariable("VerificationCode", code);
        String content = templateEngine.process("mail_forgot_template.html", context);
        emailService.sendHtml(TenantContextHolder.getTenant(), target.getEmail(), "【考级考务管理系统】验证码", content);
        log.info("发送重置密码邮件成功...");
    }


    @Transactional
    @Override
    public boolean updatePassword(Long id, String oldPassword, String newPassword) {

        boolean flag = false;
        SysUser sysUser = userDao.findById(id);
        if (StringUtils.isNotBlank(oldPassword)) {
            if (!passwordEncoder.matches(oldPassword, sysUser.getPassword())) {
                throw new BusinessException(ExceptionEnum.ILLEGAL_OLD_PASSWORD.getCode(), ExceptionEnum.ILLEGAL_OLD_PASSWORD.getMsg());
            }
        }
        if (StringUtils.isBlank(newPassword)) {
            newPassword = CommonConstant.DEF_USER_PASSWORD;
        }
        SysUser user = new SysUser();
        user.setId(id);
        user.setPassword(passwordEncoder.encode(newPassword));
        flag = userDao.updateByOps(user) > 0 ? true : false;
        return flag;
    }

    @Override
    public boolean updateEnabled(Long id, Boolean enabled) {
        boolean flag = false;
        SysUser sysUser = userDao.findById(id);

        SysUser user = new SysUser();
        user.setId(id);
        user.setEnabled(enabled);
        flag = userDao.updateByOps(user) > 0 ? true : false;
        return flag;
    }


    private SysUser getUser(List<SysUser> users) {
        SysUser user = null;
        if (users != null && !users.isEmpty()) {
            user = users.get(0);
        }
        return user;
    }

    /**
     * 给用户设置角色
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void setRoleToUser(Long id, Set<Long> roleIds) {
        SysUser sysUser = userDao.findById(id);
        if (sysUser == null) {
            throw new IllegalArgumentException("用户不存在");
        }

        userRoleDao.deleteByUser(id);
        if (!CollectionUtils.isEmpty(roleIds)) {
            SysUserRole item;
            for (Long roleId : roleIds) {
                item = new SysUserRole();
                item.setRoleId(roleId);
                item.setUserId(id);
                userRoleDao.save(item);
            }
        }
    }


    @Override
    public PageResult<SysUser> findUsers(Map<String, Object> params) {
        Page<SysUser> page = new Page<>(MapUtils.getInteger(params, CommonConstant.PAGE_CURRENT), MapUtils.getInteger(params, CommonConstant.PAGE_SIZE));
        List<SysUser> list = userDao.findList(params);


        return PageResult.<SysUser>builder().data(PageResultData.<SysUser>builder().records(list).total(page.getTotal()).pages((int) page.getPages()).build()).code(CodeEnum.SUCCESS.getCode()).count(page.getTotal()).build();

    }


    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean delUser(Long id) {
        userRoleDao.deleteByUser(id);
        return userDao.delete(id) > 0;
    }

    @Override
    public LoginAppUser getLoginAppUser(SysUser sysUser) {
        if (sysUser != null) {
            LoginAppUser loginAppUser = new LoginAppUser();
            loginAppUser.setId(sysUser.getId());
            loginAppUser.setUsername(sysUser.getUsername());
            loginAppUser.setRealname(sysUser.getRealname());
            loginAppUser.setMobile(sysUser.getMobile());
            loginAppUser.setType(sysUser.getType());
            loginAppUser.setStatus(sysUser.getStatus());
            loginAppUser.setOrganizationId(sysUser.getOrganizationId());
            loginAppUser.setCreateTime(sysUser.getCreateTime());
            loginAppUser.setDescription(sysUser.getDescription());
            loginAppUser.setEmail(sysUser.getEmail());
            loginAppUser.setDeleted(sysUser.getDeleted());
            loginAppUser.setHeadImgUrl(sysUser.getHeadImgUrl());
            loginAppUser.setPassword(sysUser.getPassword());
            loginAppUser.setEnabled(sysUser.getEnabled());
            loginAppUser.setTenantId(sysUser.getTenantId());

            loginAppUser.setRoleIds(sysUser.getRoleIds());
            loginAppUser.setRoles(sysUser.getRoles());

            HashSet permissions = new HashSet();
            List<SysFunctionPermission> scopePermissionList = functionPermissionService.findByUser(loginAppUser);
            if (org.apache.commons.collections4.CollectionUtils.isNotEmpty(scopePermissionList)) {
                for (SysFunctionPermission item : scopePermissionList) {
                    permissions.add(item.getItem().getCode());
                }
            }

            loginAppUser.setPermissions(permissions);
            return loginAppUser;
        }
        return null;
    }


}